package report

import (
	"time"

	"gddgame.cc/galaxy/asteroid"
	"gddgame.cc/galaxy/asteroid/cluster"
	"gddgame.cc/galaxy/binocle"
	"gddgame.cc/galaxy/binocle/module/report"
	. "gddgame.cc/galaxy/common/dsl/db"
	. "gddgame.cc/galaxy/common/dsl/web"
	"gddgame.cc/galaxy/server/composer"
	"gddgame.cc/galaxy/server/web"
	"gddgame.cc/galaxy/utils"
)

const PluginName = "report"

type MetricCycle = report.MetricCycle

const (
	Minute1Cycle  = report.Minute1Cycle
	Minute5Cycle  = report.Minute5Cycle
	Minute15Cycle = report.Minute15Cycle
	Minute30Cycle = report.Minute30Cycle
	Hour1Cycle    = report.Hour1Cycle
	Hour12Cycle   = report.Hour12Cycle
	Hour24Cycle   = report.Hour24Cycle
)

var (
	currentClient *cluster.Client
)

func init() {
	RegisterPlugin(PluginProvider{
		Name: PluginName,

		OnInit: func(builder composer.Builder, core binocle.CoreClient) error {
			core.MountClient(func(client *cluster.Client) {
				currentClient = client
				MountClient(builder, core, client)
				core.Watching("report/metrics", func(message asteroid.Message) {
					server := message.Data().(string)
					target := core.AllocObserver(server)
					var result interface{}
					wait := make(chan struct{}, 1)
					client.Direct(target).List(message.List()...).CallbackTo(func(success bool, data interface{}) {
						result = data
						wait <- struct{}{}
					}).Send("report/metrics")
					<-wait
					message.Response(result)
				})
				core.Watching("report/server", func(message asteroid.Message) {
					server := message.Data().(string)
					target := core.AllocObserver(server)
					var result interface{}
					wait := make(chan struct{}, 1)
					client.Direct(target).List(message.List()...).CallbackTo(func(success bool, data interface{}) {
						result = data
						wait <- struct{}{}
					}).Send("report/server")
					<-wait
					message.Response(result)
				})
				core.Watching("report/nodes", func(message asteroid.Message) {
					server := message.Data().(string)
					target := core.AllocObserver(server)
					var result interface{}
					wait := make(chan struct{}, 1)
					client.Direct(target).List(message.List()...).CallbackTo(func(success bool, data interface{}) {
						result = data
						wait <- struct{}{}
					}).Send("report/nodes")
					<-wait
					message.Response(result)
				})
			})

			AddTable(&Table{
				Model: ServerReportModel.Model,
			})
			return nil
		},
		BindRouter: BindRouter,
	})
}

func MountClient(builder composer.Builder, core binocle.CoreClient, client *cluster.Client) {
	// 绑定存储接口
	core.BindReportStore(&ServerReportModel)

	client.On("report/metrics", func(message asteroid.Message) {
		server := message.Data().(string)
		tI := utils.ToInt(message.Read(1))
		t := report.MetricType(tI)
		message.Response(core.Metrics(server, t))
	})
	client.On("report/server", func(message asteroid.Message) {
		server := message.Data().(string)
		var metrics []string
		metricsI := message.Read(1)
		if metricsI != nil {
			metrics = utils.ToStringSlice(metricsI)
		}
		cycleI := utils.ToInt(message.Read(2))
		cycle := report.MetricCycle(cycleI)
		start := utils.ToInt64(message.Read(3))
		startTime := time.Unix(start, 0)
		end := utils.ToInt64(message.Read(4))
		endTime := time.Unix(end, 0)
		message.Response(core.ReportsWithTime(server, metrics, cycle, &startTime, &endTime))
	})
	client.On("report/nodes", func(message asteroid.Message) {
		server := message.Data().(string)
		var nodes []string
		var metrics []string
		metricsI := message.Read(1)
		if metricsI != nil {
			metrics = utils.ToStringSlice(metricsI)
		}
		nodesI := message.Read(2)
		if nodesI != nil {
			nodes = utils.ToStringSlice(nodesI)
			if len(nodes) == 0 {
				nodes = core.NodeList(server)
			}
		} else {
			nodes = core.NodeList(server)
		}
		result := make([]interface{}, len(nodes))
		for key, node := range nodes {
			result[key] = core.NodeSourceReports(node, metrics)
		}
		message.Response(result)
	})
}

func BindRouter(builder composer.Builder, core binocle.CoreClient, r *SwaggerRouter) {
	r.Get("/server/{server}/report/metrics", Swagger{
		Controller: func(context web.Context, server string, dc string, t int) (interface{}, error) {
			var result interface{}
			var err error
			wait := make(chan struct{}, 1)
			core.PublishPipe().Cross(map[string]string{dc: core.Name()}).List(server, t).CallbackTo(func(success bool, data interface{}) {
				if success {
					result = data
				} else {
					err = ValidException("Target Fail")
				}
				wait <- struct{}{}
			}).Send("report/metrics")
			<-wait
			return result, err
		},
		Params: []Param{
			{Name: "server", In: Path},
			{Name: "dc", In: Query, Required: true, Default: core.DC()},
			{Name: "type", In: Query, Type: Int, Default: 0},
		},
		Response: PageResponse(nil),
	})
	r.Put("/server/{server}/report/nodes", Swagger{
		Controller: func(context web.Context, server string, dc string, nodes []string, metrics []string) (interface{}, error) {
			var result interface{}
			var err error
			wait := make(chan struct{}, 1)
			core.PublishPipe().Cross(map[string]string{dc: core.Name()}).List(server, metrics, nodes).CallbackTo(func(success bool, data interface{}) {
				if success {
					result = data
				} else {
					err = ValidException("Target Fail")
				}
				wait <- struct{}{}
			}).Send("report/nodes")
			<-wait
			return result, err
		},
		Params: []Param{
			{Name: "server", In: Path, Required: true},
			{Name: "dc", In: Body, Required: true, Default: core.DC()},
			{Name: "nodes", In: Body, Array: true},
			{Name: "metrics", In: Body, Array: true},
		},
		Response: ApiResponse,
	})
	r.Put("/server/{server}/report/server", Swagger{
		Controller: func(context web.Context, server string, dc string, metrics []string, cycle int, startTime string, endTime string) (interface{}, error) {
			var result interface{}
			var err error
			wait := make(chan struct{}, 1)
			start, err := time.Parse(time.RFC3339, startTime)
			if err != nil {
				return nil, err
			}
			end, err := time.Parse(time.RFC3339, endTime)
			if err != nil {
				return nil, err
			}
			core.PublishPipe().Cross(map[string]string{dc: core.Name()}).List(server, metrics, cycle, start.Unix(), end.Unix()).CallbackTo(func(success bool, data interface{}) {
				if success {
					result = data
				} else {
					err = ValidException("Target Fail")
				}
				wait <- struct{}{}
			}).Send("report/server")
			<-wait
			return result, err
		},
		Params: []Param{
			{Name: "server", In: Path, Required: true},
			{Name: "dc", In: Body, Required: true, Default: core.DC()},
			{Name: "metrics", In: Body, Array: true},
			{Name: "cycle", In: Body, Type: Int},
			{Name: "start", In: Body},
			{Name: "end", In: Body},
		},
		Response: ApiResponse,
	})
}
